import math
import time
import uuid
from PySide2.QtWidgets import *
from PySide2.QtGui import *
from PySide2.QtCore import *
from functools import wraps
from .cgai import CGAI
from .widgets.InputWidget import EditInput


def time_taken(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        start = time.time()
        print("Function <%s> begins." % f.__name__)
        result = f(*args, **kwargs)
        elapsed = (time.time() - start) * 1000
        print("Function <%s> took %d ms to finish." % (f.__name__,
                                                       elapsed))
        return result

    return wrapper



class PointItem(QGraphicsItem):
    
    def __init__(self):
        super(PointItem, self).__init__()
        self.__pid = None
        self.base_type='PointItem'  #声明类型
        
    @property
    def pid(self):
        """
        当前item的id
        :return:
        """
        if not self.__pid:
            self.__pid=str(uuid.uuid1())
        return self.__pid

    def addLineItem(self,*args):
        """
        添加关联的LineItem的id,仅当LineItem完全相连时存入
        :param args: 
        :return: 
        """
        pass

    def removeLineItem(self,lid):
        """
        移除已存入的LineItem只需要lid即可
        :param lid:
        :return:
        """
        pass

    def set_offset_pos(self,pos):
        """
        设置与主节点的偏移坐标
        :param pos: 
        :return: 
        """
        pass


    # def get_connect_bodys(self):
    def body(self):
        """
        获取当前连点的所相连的bodyitem
        :return:
        """
        pass





class BodyItem(QGraphicsItem):
    """
    中间主信息节点基类
    必备功能：1.通过主节点能查询到自身的连点
             2.通过主节点能查询到所有相关的连线连点
    """
    name = 'body'
    def __init__(self):
        super(BodyItem, self).__init__()
        self._bid=None
        self.base_type = 'BodyItem'  #声明类型


    @property
    def bid(self):
        """
        当前item的id
        :return:
        """
        if not self._bid:
            self._bid=str(uuid.uuid1())
        return self._bid

    def data(self):
        """
        当前节点包含的数据
        :return:
        """
        pass

    def function(self,*args,**kwargs):
        """
        执行函数
        :param args:
        :param kwargs:
        :return:
        """
        pass

    def set_input_pitems(self,*args):
        """
        实例后，添加输入连接点
        :param args:
        :return:
        """
        pass

    def set_output_pitems(self,*args):
        """
        实例后,添加输出连接点
        :param args:
        :return:
        """
        pass

    def get_line_items(self):
        """
        获取当前连接点的所以连接LineItem
        :return:
        """
        pass

    def get_lids(self):
        """
        获取当前所有相连的连线lid
        :return:
        """
        pass

    def get_up_items(self):
        """
        获取顶端输入节点
        :return:
        """
        pass
    def get_left_items(self):
        """
        获取左边输入节点
        :return:
        """
        pass

    def get_down_items(self):
        """
        获取底端输出节点
        :return:
        """
        pass

    def get_right_items(self):
        """
        获取右端输出节点
        :return:
        """
        pass

    def get_up_data(self):
        """
        获取上方输入节点数据
        :return:
        """
        pass

    def get_left_data(self):
        """
        获取下方输入节点数据
        :return:
        """
        pass

    def get_right_data(self):
        """
        获取右边输出数据
        :return:
        """
        pass

    def get_down_data(self):
        """
        获取下边输出数据
        :return:
        """
        pass

    def get_pids(self):
        """
        获取该节点所有连接点的id
        :return: []
        """
        pass

    def merge_input_data(self):
        """
        合并输入数据
        :return:
        """
        pass

    def update_data(self):
        """
        每次切换输入后
        :return:
        """
        pass

    def setName(self,*args):
        """
        :param args:
        :return:
        """
        pass

    def set_status(self,*args):
        """
        设置状态
        :param args:
        :return:
        """
        pass

    def setIgnore(self,*args):
        """
        忽略数据计算
        :param args:
        :return:
        """
        pass

    def execute(self):
        """
        执行计算
        :return:
        """
        pass

    def pass_through(self):
        """
        忽略自身数据计算
        :return:
        """
        pass

class IPointItem(PointItem):
    """
    起始连点
    """
    def __init__(self,port,parent=None):
        super(IPointItem, self).__init__()
        self.port=port  #连接端口，左或者上方向
        self.parent = parent
        self.offset_pos=None  #相对于父节点的偏移坐标
        if parent:
            self.setParentItem(parent)
        self.radius = 20

        self.line_items = {}   #存入相连关联的LineItem  {'lid':Line_items}
        # self.connect_pitems = []  #已经连接的其他连接点
        self.__pid = None

        self.setFlag(QGraphicsItem.ItemIsSelectable, True)  #只有开启了能被选中,开始画线时不会出现框选

        self.setToolTip('Input')

    def __repr__(self):
        return 'IPointItem:{}'.format(self.pid)

    @property
    def itype(self):
        """
        该连接点的类型,可以用来区分节点的连接是否输入输出正确
        :return:
        """
        return 'IPointItem'

    def body(self):
        return self.parent

    #即时处理函数
    def addLineItem(self,lineItem):
        """
        在创建连线后及时添加,添加关联的LineItem的id,仅当LineItem完全相连时存入
        :param lineItem:
        :return:
        """
        self.line_items[lineItem.lid] = lineItem

    def removeLineItem(self,lid):
        """
        移除已存入的LineItem只需要lid即可
        :param lid:
        :return:
        """
        self.line_items.pop(lid)


    # def add_connect_pitem(self,pitem):
    #     """
    #     添加关联的连接点
    #     :param pitem:
    #     :return:
    #     """
    #     if pitem not in self.connect_pitems:
    #         self.connect_pitems.append(pitem)


    def set_offset_pos(self,pos):
        self.offset_pos = pos

    def boundingRect(self, *args, **kwargs):
        return QRectF(0, 0, self.radius, self.radius)

    def paint(self, QPainter, QStyleOptionGraphicsItem, widget=None):
        QPainter.setBrush(QBrush(QColor('#C6E2FF')))
        QPainter.drawEllipse(0, 0, self.radius, self.radius)

    def mousePressEvent(self, QMouseEvent):
        if QMouseEvent.button() == Qt.LeftButton:
            print('点到IPointItem')
            print('连点坐标:',self.pos())
            mapToScene = self.mapToScene(self.pos())
            print('连点mapToScene',mapToScene)
            print('连点mapFromScene - self.pos():',mapToScene-self.pos())
            super(IPointItem, self).mousePressEvent(QMouseEvent)

    def mouseReleaseEvent(self, QMouseEvent):

        super(IPointItem, self).mouseReleaseEvent(QMouseEvent)


    def ungrabMouse(self,QMouseEvent):

        print('AAAA')

        super(IPointItem, self).ungrabMouse(QMouseEvent)

    # def isUnderMouse(self,QMouseEvent):
    #     print('AAAA')
    #
    #
    #     super(IPointItem, self).isUnderMouse(QMouseEvent)


class OPointItem(PointItem):
    """
    结束连点
    """
    def __init__(self,PORT,parent=None):
        super(OPointItem, self).__init__()
        self.port = PORT  # 连接端口，左或者上方向
        self.parent = parent
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.offset_pos = None  # 相对于父节点的偏移坐标
        if parent:
            self.setParentItem(parent)
        self.radius=20
        self.__pid=None
        self.line_items ={}   #存入相连关联的LineItem  {'lid':Line_items}

        self.setFlag(QGraphicsItem.ItemIsSelectable, True)  # 只有开启了能被选中,开始画线时不会出现框选
        self.setToolTip('Output')

    def __repr__(self):
        return 'OPointItem:{}'.format(self.pid)

    @property
    def itype(self):
        """
        该连接点的类型
        :return:
        """
        return 'OPointItem'


    def addLineItem(self,lineItem):
        """
        添加关联的LineItem的id,仅当LineItem完全相连时存入
        :param lineItem:
        :return:
        """
        self.line_items[lineItem.lid]=lineItem

    def removeLineItem(self,lid):
        """
        移除已存入的LineItem只需要lid即可
        :param lid:
        :return:
        """
        self.line_items.pop(lid)

    def set_offset_pos(self,pos):
        self.offset_pos=pos

    def boundingRect(self, *args, **kwargs):
        return QRectF(0, 0, self.radius, self.radius)

    def paint(self, QPainter, QStyleOptionGraphicsItem, widget=None):
        QPainter.setBrush(QBrush(QColor('#AB82FF')))
        QPainter.drawEllipse(0, 0, self.radius, self.radius)

    def mousePressEvent(self, QMouseEvent):
        if QMouseEvent.button() == Qt.LeftButton:
            print('点到OPointItem')
            print('连点坐标:',self.pos())
            super(OPointItem, self).mousePressEvent(QMouseEvent)

    def mouseReleaseEvent(self, QMouseEvent):

        super(OPointItem, self).mouseReleaseEvent(QMouseEvent)

class StatusItem(QGraphicsItem):
    """
    状态显示节点.
    节点只有三种状态。
    Wait: 未执行
    Running: 正在执行
    Completed: 已完成
    """
    def __init__(self,parent=None):
        super(StatusItem, self).__init__()
        self.parent = parent
        if parent:
            self.setParentItem(parent)
        self.base_type = 'StatusItem'
        self.color_map = {CGAI.WAIT:CGAI.WAIT_COLOR,CGAI.RUNNING:CGAI.RUNNING_COLOR,
                          CGAI.COMPLETED:CGAI.COMPLETED_COLOR,CGAI.ERROR:CGAI.ERROR_COLOR}
        # self.radius=10
        self.width = 30
        self.height = 10
        self.__status = CGAI.WAIT

        self.setFlag(QGraphicsItem.ItemIsSelectable, False)
        self.setToolTip(self.__status)

    @property
    def status(self):
        return self.__status

    def set_status(self,status):
        self.__status = status
        self.update()
        self.setToolTip(self.__status)

    def boundingRect(self, *args, **kwargs):
        # return QRectF(0, 0, self.radius, self.radius)
        return QRectF(0, 0, self.width, self.height)

    def paint(self, QPainter, QStyleOptionGraphicsItem, widget=None):
        color= self.color_map.get(self.__status,CGAI.WAIT_COLOR)
        QPainter.setBrush(QBrush(QColor(color)))
        # QPainter.drawEllipse(0, 0, self.radius, self.radius)
        QPainter.drawRoundedRect(self.boundingRect(), 5, 5)

class LineItem(QGraphicsPathItem):
    """
    连线节点，每次当成功创建连接起始与结束节点的LineItem时，给该item添加一个唯一id,并且将该id存入，连接的起始与结束节点的body里
    必备功能：通过连线节点可以查询到相连的连点，以及对应的主节点
    连线是有方向：它的起始输入连点item必为IPointItem,结束连点必为OPointItem，但是它本身存储的self.__start_pid只是起始绘制时，
                鼠标选择的点，而不一定是IPointItem。同理self.__end_pid,它们只是为了绘制线提供的计算坐标。
                而self.__start_point_item与self.__end_point_item是经过验证连点类型后的，它们是对应输入输出类型的。
    
    """
    def __init__(self,nodegraph,start_pid,end_pid=None):
        """
        传入nodegraph方便取全局item信息,传入连线的起始连点pid与结束连点pid，如果传入item那好当创建新变量赋值该item后，
        会新开辟了内存，导致不会实时随item变化而更新数据

        结束连点可以为空，仅当生成最后完整联线时，可以有具体end_pitem.
        当没有end_pid时表示开始拉线，还没连到结束连点
        :param start_pitem:
        :param end_pitem:
        """
        super(LineItem, self).__init__()

        self.nodegraph = nodegraph  #node_graph
        self.base_type = 'LineItem'
        self.width = 1.5
        self.start_pos = QPointF(0,0) if not start_pid else self.relative_parent_pos(self.nodegraph.all_point_items[start_pid])   #起始坐标
        self.end_pos = self.start_pos if not end_pid else self.relative_parent_pos(self.nodegraph.all_point_items[end_pid])   #结束坐标
        self.__start_pid = start_pid  # 这里的起始pid不一定是IPointItem类型，只是起始绘制点
        self.__end_pid = end_pid  #这里的结束pid不一定是OPointItem类型

        self.__start_point_item = None  #起始连点，这里是经过验证后的起始item,必为IPointItem
        self.__end_point_item = None    #结束连点，经验证后的结束item,必为OPointItem
        self.resort_in_out_item(self.__start_pid,self.__end_pid)

        self.__lid = ''
        self.default_color = '#E8E8E8'
        self.selecting_color = '#ff8f05'
        self.pen = QPen(QColor(self.default_color))
        self.pen.setWidthF(self.width)
        self.selecting = False
        # self.setFlag(QGraphicsItem.ItemIsSelectable)
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.setZValue(-1)


    @property
    def lid(self):
        """
        当前LineItem的唯一id
        :return:
        """
        if not self.__lid:
            self.__lid = str(str(uuid.uuid1()))
        return self.__lid

    @property
    def itype(self):
        """
        该连线的item类型标识
        :return:
        """
        return 'LineItem'

    def resort_in_out_item(self,start_pid,end_pid):
        """
        重新排序自身输入输出节点
        :param args:
        :return:
        """
        start_point_item = self.nodegraph.all_point_items.get(start_pid,None)
        end_point_item = self.nodegraph.all_point_items.get(end_pid,None)
        if isinstance(start_point_item,IPointItem):  #当连点是输入端，那么线则视为结束端
            self.__start_point_item = end_point_item
            self.__end_point_item = start_point_item

        else:  #当连点输出点，则线视为起始端
            self.__start_point_item = start_point_item
            self.__end_point_item = end_point_item

    def get_pids(self):
        """
        获取两个连接点的pids
        :return:
        """
        start_pid = self.__start_point_item.pid
        end_pid = self.__end_point_item.pid

        return [start_pid,end_pid]



    def get_input_item(self):
        """
        获取输入连接起始主节点
        :return:
        """
        # return self.nodegraph.all_point_items[self.start_pid].parent if self.start_pid else None
        return self.__start_point_item.parent


    def get_output_item(self):
        """
        获取输入连接结束主节点
        :return:
        """
        # return self.nodegraph.all_point_items[self.end_pid].parent if self.end_pid else None
        return self.__end_point_item.parent

    def bind_point_item(self,start_pitem,end_pitem):
        """
        绑定起始连点与结束连点
        :param start_pitem:
        :param end_pitem:
        :return:
        """
        start_pitem.addLineItem(self)
        end_pitem.addLineItem(self)


    def relative_parent_pos(self,item):
        """

        通过传入连接点,获取相对父节点的位置，用于校正偏移
        传入的是连点，因为连点已经设置了父节点，所以使用pos计算出来的都是相对于父节点的位置
        :param item:
        :return:
        """

        p_pos = item.parent.pos()# mapToScene(item.pos())
        offset_pos = item.offset_pos
        pos = p_pos+offset_pos

        return pos

    def break_points(self):
        """
        清空两个连接点上对该线的记录
        :return:
        """
        # input_item = self.get_input_item()
        # output_item = self.get_output_item()

        self.__start_point_item.removeLineItem(self.lid)
        self.__end_point_item.removeLineItem(self.lid)


    # def relative_parent_pos(self,b_pos):
    #     """
    #     获取相对父节点的位置，用于校正偏移
    #     传入的是连点，因为连点已经设置了父节点，所以使用pos计算出来的都是相对于父节点的位置
    #     :param b_pos:
    #     :return:
    #     """
    # 
    #     p_pos=item.parent.pos()# mapToScene(item.pos())
    #     offset_pos=item.offset_pos
    #     pos= p_pos+offset_pos
    # 
    #     return pos


    def set_startpos(self,pos):
        """
        绘制关键函数，设置起始坐标，可以实时绘制起始位置
        :param pos:
        :return:
        """
        self.start_pos=pos

    def set_endpos(self,pos):
        """
        绘制关键函数，设置结束坐标，可以实时绘制最终位置
        :param pos:
        :return:
        """
        self.end_pos=pos

    def draw_path(self):
        path = QPainterPath(self.start_pos)
        path.lineTo(self.end_pos.x(),self.end_pos.y())

        return path

    def boundingRect(self, *args, **kwargs):
        return self.shape().boundingRect()

    def shape(self, *args, **kwargs):
        return self.draw_path()

    def paint(self, painter, graphics_item, widget=None):
        if self.isSelected() or self.selecting:
        # painter.setBrush(QBrush(QColor('#ff8f05')))
            self.pen.setColor(QColor(self.selecting_color))  # F4A460
            self.pen.setWidth(self.width+0.5)
        else:
            self.pen.setColor(QColor(self.default_color))
            self.pen.setWidth(self.width)

        self.setPath(self.draw_path())
        path = self.path()
        if self.__end_pid:  #当存在有最终连点时 绘制最终连线
            painter.setPen(self.pen)
            painter.drawPath(path)
        else: #没有存在存在最终连点时，拉线
            painter.setPen(self.pen)
            painter.drawPath(path)

    def mousePressEvent(self, QMouseEvent):
        if QMouseEvent.button() == Qt.LeftButton:
            self.selecting = False
        super(LineItem, self).mousePressEvent(QMouseEvent)


    # @time_taken
    def update_pos(self):
        """
        刷新路径线的位置
        :param b_pos: 主节点坐标
        :return:
        """
        # start_width=self.start_pitem.radius/2
        start_pos = self.relative_parent_pos(self.nodegraph.all_point_items[self.__start_pid])
        self.set_startpos(start_pos)
        if self.__end_pid:  #当存在最终连点时，那么就要计算最终连点的场景坐标了
            end_pos = self.relative_parent_pos(self.nodegraph.all_point_items[self.__end_pid])
            self.set_endpos(end_pos)
        else:
            self.set_endpos(start_pos)
        # self.update()


class CGAIItem(BodyItem):
    """
    中间主信息显示节点,输入，输出连点都是其子item,

    """
    node_type = 'cgai'
    def __init__(self,nodegraph,node_name,data=None):
        super(CGAIItem, self).__init__()

        self.nodegraph =nodegraph
        self.__node_name = node_name
        #这部分属性后面可能会写在data中，方便批量处理
        self.width = 150  #节点宽度
        self.height = 50  #节点高度
        self.__input_pcounts = 4   #输入端口数量，从左往右，最左边是索引0
        self.__output_pcounts = 2  #输出端口数量，从左往右，下边的左边是索引0
        self.__color = '#FA8072' #节点颜色
        self.font = QFont('',15,55)  #字体大小

        #节点内部数据
        self.__data = None


        self.selecting = False  #是否被选择中

        self.offset_pos = QPointF(self.width/2,self.height/2)  #体积偏移坐标，当在创建节点时,减去该值会在鼠标正中间

        self.input_pitems = {} #BodyItem的输入连接点item  列表元素不会新开复制新开内存空间
        self.output_pitems = {}  #BodyItem输出的连接点item的pid
        self.status_item = None #状态item

        self.__status = CGAI.WAIT  #当前节点的状态
        self.__branch = False  #是否数据分流
        self.__ignore = False  # 是否忽略计算

        self.setToolTip(self.name)
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QGraphicsItem.ItemIsMovable, True)



    def __repr__(self):

        return 'CGAIItem'

    @property
    def itype(self):
        """
        该节点的类型,可以用来区分节点的连接是否输入输出正确
        :return:
        """
        return 'CGAIItem'

    def function(self,*args,**kwargs):
        pass

    @property
    def status(self):
        """
        当前节点状态
        :return:
        """

        return self.__status

    def set_status(self,status):
        """
        设置当前状态
        :param status:
        :return:
        """
        self.__status = status
        self.status_item.set_status(status)

    def setName(self,name):
        """
        设置节点名称
        :param name:
        :return:
        """
        self.__node_name = name


    # @property
    def data(self):
        """
        获取当前item数据
        :return:
        """
        return self.__data

    def get_input_pcounts(self):
        """
        获取输入的端点数量
        :return:
        """
        return self.__input_pcounts

    def get_output_pcounts(self):
        """
        获取输出的端点数量
        :return:
        """
        return self.__output_pcounts

    def get_up_items(self):
        """
        获取上方端口输入节点
        :return: {1:[BodyItem,BodyItem,...,],2:BodyItem,...}
        """
        items = {}
        for k,v in self.input_pitems.items():  #获取输出点item
            if k>0:
                line_items = v.line_items  #获取每个输入的连线，注意该连线是一个字典
                for line_item in line_items.values():
                    input_item = line_item.get_input_item() #因为是连入点，所以是线的结束端,那么就找线的输入起始端的item
                    items.append(input_item)
        return items

    def get_left_items(self):
        """
        获取左边端口输入节点
        :return:
        """
        pitem = self.input_pitems[0]
        items = []
        line_items = pitem.line_items  #获取每个输入的连线，注意该连线是一个字典
        for line_item in line_items.values():
            input_item = line_item.get_input_item() #因为是连入点，所以是线的结束端,那么就找线的输入起始端的item
            items.append(input_item)
        return items




    def get_up_data(self):
        """
        获取上方输入节点数据
        :return:
        """
        pass



    def get_left_data(self):
        """
        获取下方输入节点数据
        :return:
        """
        pass

    def get_right_data(self):
        pass

    def get_down_data(self):
        pass


    def get_pids(self):
        pid_list = []
        for i in self.input_pitems.values():
            pid = i.pid
            pid_list.append(pid)

        for o in self.output_pitems.values():
            pid = o.pid
            pid_list.append(pid)

        return pid_list

    def merge_input_data(self):
        right_data=self.get_right_data()



    def set_input_pitems(self,items_dict):
        """
        实例后，添加输入连接点
        :param items_dict:
        :return:
        """
        
        # self.input_pitems = [i for i in args]
        self.input_pitems = items_dict

    def set_output_pitems(self,items_dict):
        """
        实例后,添加输出连接点
        :param items_dict:
        :return:
        """
        # self.output_pitems = [i for i in args]
        self.output_pitems = items_dict

    def set_status_item(self,statusItem):
        self.status_item=statusItem

    def get_line_items(self):
        """
        获取当前连接点的所有连接LineItem
        :return:
        """
        line_items=[]
        if self.input_pitems:
            for i in self.input_pitems.values():
                if i.line_items:
                    line_items.extend(i.line_items.values())
        if self.output_pitems:
            for o in self.output_pitems.values():
                if o.line_items:
                    line_items.extend(o.line_items.values())
        return line_items

    def get_lids(self):
        """
        获取所有相连的连线lid
        :return:
        """
        lids_list = []
        line_litems = self.get_line_items()
        for i in line_litems:
            lid = i.lid
            lids_list.append(lid)

        return lids_list


    # @time_taken
    def update_lineItmes_pos(self):
        """
        实时更新line_items的位置
        :param b_pos: 主节点坐标
        :return:
        """
        if self.line_items:
            for i in self.line_items:
                i.update_pos()

    def boundingRect(self, *args, **kwargs):

        return QRectF(0,0,self.width,self.height)

    def paint(self, QPainter,QStyleOptionGraphicsItem, widget=None):

        pen = QPen()

        if self.isSelected() or self.selecting:
            pen.setColor(QColor('#FFFFF0'))  # F4A460
            pen.setWidth(2)

        else:
            pen.setColor(QColor('#363636'))
            pen.setWidth(2)
        QPainter.setPen(pen)
        QPainter.setFont(self.font)
        QPainter.setBrush(QBrush(QColor(self.__color)))
        QPainter.drawRoundedRect(self.boundingRect(), 15, 15)

        QPainter.drawText(self.boundingRect(), Qt.AlignCenter, self.__node_name)


    def mousePressEvent(self, QMouseEvent):
        print('body press!')
        if QMouseEvent.button() == Qt.LeftButton:
            self.selecting = False
            self.line_items = self.get_line_items()
            # print('BodyItem')
            print('BodyItem.pos():',self.pos())
            print('BodyItem mapToScene - self.pos():',self.mapToScene(self.pos())-self.pos())
            # self.clearFocus()
        super(CGAIItem, self).mousePressEvent(QMouseEvent)

    def mouseDoubleClickEvent(self, QMouseEvent):
        e_pos = QMouseEvent.pos()
        s_pos = self.mapToScene(e_pos)
        pos = QPoint(int(s_pos.x()),int(s_pos.y()))

        c_pos = QCursor.pos()
        # w_pos = self.nodegraph.mapFromGlobal(e_pos)

        # self.name_edit = EditInput()
        # self.name_edit.show()
        # self.name_edit.move(c_pos)
        self.nodegraph.edit_window.setHidden(False)# = EditInput()
        # self.nodegraph.edit_window.show()
        self.nodegraph.edit_window.move(c_pos)

        super(CGAIItem, self).mouseDoubleClickEvent(QMouseEvent)

    def mouseMoveEvent(self,QMouseEvent):
        self.update_lineItmes_pos()
        print('mouseMove !!')
        super(CGAIItem, self).mouseMoveEvent(QMouseEvent)

    def execute(self):
        pass

    def pass_through(self):
        pass

    def setIgnore(self,ignore):
        if ignore:
            self.pass_through()
        else:
            self.execute()

    # def mouseMoveEvent(self, QMouseEvent):
    #     if self.isUnderMouse():
    #         # print('在鼠标下面运动')
    #         # e_pos=QMouseEvent.pos()
    #         # s_pos=self.mapToScene(e_pos)
    #         self.update_lineItmes_pos()  #根据主节点的位置来更新连线位置
    #             # print('更新连线位置')
    #     super(CGAIItem, self).mouseMoveEvent(QMouseEvent)




    # def mouseDoubleClickEvent(self, QMouseEvent):
    #     pass
    #
    #     super(CGAIItem, self).mouseDoubleClickEvent(QMouseEvent)
    #
    # def mouseReleaseEvent(self, QMouseEvent):
    #     pass
    #
    #     super(CGAIItem, self).mouseReleaseEvent(QMouseEvent)
    #


class DataFrameItem(BodyItem):
    """
    pandas.DataFrame节点，初始化DataFrame
    """
    node_type = 'dataframe'
    import pandas as pd

    def __init__(self, nodegraph):
        super(DataFrameItem, self).__init__()

        self.nodegraph = nodegraph

        self.width = 150
        self.height = 50
        self.selecting = False
        self.offset_pos = QPointF(self.width / 2, self.height / 2)  # 体积偏移坐标，当在创建节点时,减去该值会在鼠标正中间

        self.input_pitems = []  # BodyItem的输入连接点item  列表元素不会新开复制新开内存空间
        self.output_pitems = []  # BodyItem输出的连接点item的pid
        self.status_item = None  # 状态item

        self.__status = CGAI.WAIT  # 当前节点的状态

        self.font = QFont('', 15, 55)
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QGraphicsItem.ItemIsMovable, True)

    def __repr__(self):

        return 'DataFrameItem'

    @property
    def itype(self):
        """
        该节点的类型,可以用来区分节点的连接是否输入输出正确
        :return:
        """
        return 'DataFrameItem'



    @property
    def status(self):
        """
        当前节点状态
        :return:
        """

        return self.__status

    def set_status(self, status):
        """
        设置当前状态
        :param status:
        :return:
        """
        self.__status = status
        self.status_item.set_status(status)

    def set_input_pitems(self, *args):
        """
        实例后，添加输入连接点
        :param args:
        :return:
        """

        self.input_pitems = [i for i in args]

    def set_output_pitems(self, *args):
        """
        实例后,添加输出连接点
        :param args:
        :return:
        """
        self.output_pitems = [i for i in args]

    def set_status_item(self, statusItem):
        self.status_item = statusItem

    def get_line_items(self):
        """
        获取当前连接点的所以连接LineItem
        :return:
        """
        line_items = []
        if self.input_pitems:
            for i in self.input_pitems:
                if i.line_items:
                    line_items.extend(i.line_items.values())
        if self.output_pitems:
            for o in self.output_pitems:
                if o.line_items:
                    line_items.extend(o.line_items.values())
        return line_items

    # @time_taken
    def update_lineItmes_pos(self):
        """
        实时更新line_items的位置
        :param b_pos: 主节点坐标
        :return:
        """
        if self.line_items:
            for i in self.line_items:
                i.update_pos()

    def boundingRect(self, *args, **kwargs):

        return QRectF(0, 0, self.width, self.height)

    def paint(self, QPainter, QStyleOptionGraphicsItem, widget=None):

        pen = QPen()

        if self.isSelected() or self.selecting:
            pen.setColor(QColor('#FFFFF0'))  # F4A460
            pen.setWidth(2)

        else:
            pen.setColor(QColor('#363636'))
            pen.setWidth(2)
        QPainter.setPen(pen)
        QPainter.setFont(self.font)
        QPainter.setBrush(QBrush(QColor('#B0E2FF')))
        QPainter.drawRoundedRect(self.boundingRect(), 15, 15)

        QPainter.drawText(self.boundingRect(), Qt.AlignCenter, 'DataFrame')

    def mousePressEvent(self, QMouseEvent):
        if QMouseEvent.button() == Qt.LeftButton:
            self.selecting = False
            self.line_items = self.get_line_items()
            # print('BodyItem')
            # self.clearFocus()
        super(CGAIItem, self).mousePressEvent(QMouseEvent)




class TestItem(BodyItem):
    node_type = 'test'
    def __init__(self):
        super(TestItem, self).__init__()


ALL_ITEMS = dict((cls.node_type, cls) for cls in BodyItem.__subclasses__())